//
// @Author: zhengqi Wu
// @Description:
// @Version: 1.0.0
// @Date: 2021/1/27 19:30
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//


//
// 表示一个MIN拉取式数据包
//
// @Description:
// 	1. 包格式的定义如下
//		Data = 7 TLV-LENGTH
//             { DataIdentifier }            => 标识区
//             { Signature }                 => 签名区
//             {                             => 只读区
//                 [FreshnessPeriod]
//				   [NackHeader]
//                 <Payload>
//             }
//             {                             => 可变区
//                 {                         => 受保护区
//                     [CongestionMark]
//                 }
//                 {                         => 非受保护区
//                     [TTL]
//                 }
//             }
//

const MINPacket = require('./MINPacket')
const encoding = require('../encoding')
const {createContentDataIdentifierByComponents} = require("../component/IdentifierWrapper");
const {Payload} = require('../component/Payload')
const {CongestionMark} = require('../component/CongestionMark')
const {FreshnessPeriod} = require('../component/FreshnessPeriod')
const {TTL} = require('../component/TTL')
const {Identifier} = require('../component/Identifier')



class Data{
    constructor() {
        this.MINPacket = new MINPacket()
        this.SelfEncodingBase = new encoding.SelfEncodingBase()
        this.FreshnessPeriod = new FreshnessPeriod()
        this.Payload = new Payload()
        this.CongestionMark = new CongestionMark()
        this.TTL = new TTL()
        this.Name = new Identifier()
    }

    /**
     * 展示数据包的URI
     * @returns {string}
     */
    toUri(){
        return this.Name.toUri()
    }

    /**
     * 获取数据包的标识
     * @returns {Identifier}
     */
    getName(){
        return this.Name
    }

    /**
     * 设置内容数据包的标识
     * @param identifier {Identifier}
     */
    setName(identifier){
        this.Name = identifier
    }

    fillDataToFields() {
        this.doFillDataToFields(this.MINPacket)
    }

    doFillDataToFields(MINPacket){
        /////////////////////////////////////////////////////////////
        //// 填充可变区
        //             {                             => 可变区
        //                 {                         => 受保护区
        //                     [CongestionMark]
        //                 }
        //                 {                         => 非受保护区
        //                     "Empty"
        //                 }
        //             }
        /////////////////////////////////////////////////////////////
        try {
            // 填充可变受保护区
            MINPacket.MutableField.mutableProtectField.clearBlocks()

            // CongestionMark
            let block = this.CongestionMark.selfWireEncode(this.CongestionMark)
            MINPacket.MutableField.mutableProtectField.addBlock(block)

            // 填充可变非受保护区
            MINPacket.MutableField.mutableDangerousField.clearBlocks()
            block = this.TTL.selfWireEncode(this.TTL)
            MINPacket.MutableField.mutableDangerousField.addBlock(block)

            /////////////////////////////////////////////////////////////
            //// 填充只读区
            //             {                             => 只读区
            //                 [FreshnessPeriod]
            //				   [NackHeader]
            //                 <Payload>
            //             }
            /////////////////////////////////////////////////////////////
            MINPacket.ReadOnlyField.clearBlocks()

            // FreshnessPeriod
            if (this.FreshnessPeriod.isInitial()) {
                let block = this.FreshnessPeriod.selfWireEncode(this.FreshnessPeriod)
                MINPacket.ReadOnlyField.addBlock(block)
            }
            // NackHeader
            // if(this.NackHeader.isInitial()){
            //     let block = this.NackHeader.selfWireEncode(this.NackHeader)
            //     this.MINPacket.ReadOnlyField.addBlock(block)
            // }
            // Payload
            let pblock = this.Payload.selfWireEncode(this.Payload)
            MINPacket.ReadOnlyField.addBlock(pblock)

            /////////////////////////////////////////////////////////////
            //// 填充标识区
            /////////////////////////////////////////////////////////////
            MINPacket.IdentifierField.clearIdentifiers()
            let newIdentifierWrapper = createContentDataIdentifierByComponents(this.Name.getComponents())
            MINPacket.IdentifierField.addIdentifier(newIdentifierWrapper)
        } catch (e) {
            throw e
        }
        
    }

    doExtraDataFromFields(MINPacket){
        /////////////////////////////////////////////////////////////
        //// 解析可变区
        //             {                             => 可变区
        //                 {                         => 受保护区
        //                     [CongestionMark]
        //                 }
        //                 {                         => 非受保护区
        //                     "Empty"
        //                 }
        //             }
        /////////////////////////////////////////////////////////////
        try {
            let block = MINPacket.MutableField.mutableProtectField.getBlockByType(encoding.tlv.ComponentType.TlvCongestionMark)
            if (block === undefined) {
                throw new DataError("CongestionMark missed.")
            }
            this.CongestionMark.wireDecode(block)

            //TTL
            block = MINPacket.MutableField.mutableDangerousField.getBlockByType(encoding.tlv.ComponentType.TlvTTL)
            if (block === undefined) {
                throw new DataError("TTL missed.")
            }
            this.TTL.wireDecode(block)
            /////////////////////////////////////////////////////////////
            //// 解析只读区
            //             {                             => 只读区
            //                 [FreshnessPeriod]
            //                 <Payload>
            //             }
            /////////////////////////////////////////////////////////////
            let elements = MINPacket.ReadOnlyField.getBlocks()
            for (let i = 0; i < elements.length; i++) {
                let element = elements[i]
                switch (element.getType()) {
                    case encoding.tlv.ComponentType.TlvFreshnessPeriod:
                        this.FreshnessPeriod.wireDecode(element)
                        break
                    case encoding.tlv.ComponentType.TlvPayload:
                        this.Payload.wireDecode(element)
                        break
                    default:
                    // TODO: 这边可以规定哪些范围是自定义的，哪些是预留的，然后根据需要这边抛出错误
                }
            }

            /////////////////////////////////////////////////////////////
            //// 解析标识区
            /////////////////////////////////////////////////////////////
            let interestIdentifier = MINPacket.IdentifierField.getIdentifierByType(encoding.tlv.ComponentType.TlvIdentifierContentData)
            if (interestIdentifier === null) {
                throw new DataError("Identifier missed.")
            }
            this.setName(interestIdentifier.getIdentifier())
        } catch (e) {
            throw e
        }
        
    }

    extraDataFromFields() {
        this.doExtraDataFromFields(this.MINPacket);
    }

    /**
     * 将 Data 线速编码为一个 TLV
     * @param enc {encoding.Encoder}
     * @returns 编码长度 {number}
     */
    wireEncode(enc) {
        try {
            // 首先将各项属性填充到分区当中
            this.fillDataToFields()
            // 指定包类型
            this.MINPacket.packetType = encoding.tlv.PacketType.TlvPacketMINCommon
            return this.MINPacket.wireEncode(enc)
        } catch (e) {
            throw e
        }
    }

    /**
     * 从 TLV Block 中解码出一个 Data
     * @param block {encoding.Block}
     * @description 解码顺序： block -> MINPacket -> Data
     */
    wireDecode(block) {
        try {
            this.MINPacket.wireDecode(block)
            this.extraDataFromFields()
        } catch (e) {
            throw e
        }
        
    }
}

/**
 * createDataByMINPacket
 * @param minPacket
 * @return {Data}
 */

function createDataByMinPacket(minPacket) {
    let data = new Data()
    try {
        data.doExtraDataFromFields(minPacket)
        return data
    } catch (e) {
        throw e
    }
    
}

class DataError extends Error {
    constructor(m) {
        super(m)
        this.name = 'DataError'
    }

    error() {
        console.log(`${this.name}: ${this.message}`)
    }
}
module.exports = {
    Data: Data,
    createDataByMinPacket: createDataByMinPacket
}